rm(list = ls(all = TRUE))
setwd("C:\\Users\\lenovo\\Desktop\\R\\神经网络_R语言实现")

#感知机（perceptron）定义为神经网络的基本构建快
#在机器学习中，感知机是二值分类器的有监督学习算法
#它们将输出分类为两类值：TRUE/FALSE;0/1

#4.1 感知机及其应用
#感知机可以认为是任何需要多个输入与一个输出的东西
#这是神经网络最简单的形式

#于1958年由Frank Rosenblatt提出
#是一种具有输入层和输出层以及基于最小化误差的学习规则和实体
#这种称为误差反向传播（error backpropagation）的学习函数
#根据相对于给定输入的实际输出，改变连接权重（突触）
#即实际输入与期望输出之间的差距

#后来，科学家Marvin Minsky和Seymour Papert（1969）证明了
#感知机技能识别线性可分的函数
#解决方法：多层的感知机网络

#在单一形式中，感知机由一个神经元单位接收输入并产生一组输出
#在没有反向传播的情况下，
#感知机仅是直接进行预测的简单分类函数
#函数的核心在于权重，以及如何更新权重以最好地进行预测
#这类感知机是简单感知机（simple perceptron）
#或基本感知机（basic perceptron）
#输出为二值

#还有另一种类型的感知机：多分类感知机（multi-class perceptron）

#通过修改权重向量，可以修改感知机的输出来提高学习或存储性能
#例如，可以尝试指示一个感知机，使得给定一个输入x，
#输出y尽量接近给定的先验选择的y实际值
#然而，一个简单感知机的计算能力是有限的，
#并且可以获得的性能在很大概率上取决于输入的选择和想要执行的函数的选择

#实际上，输入可以被限制为所有可能输入的子集，
#或者可以根据某个预定的概率分布被随机地提取
#在较小的程度上，这样一个系统的性能还取决于
#实际输出与预期输出之间的距离是i如何被量化的


#4.2 简单感知机——一个线性可分离分类器

#最简单的简单感知机是一个单层神经元，它是一个线性神经元，
#可以在激活（active）或非激活（inactive）的情况下合成
#其决策规则是通过阈值（threshold）实现的
#加权总和超过阈值则激活并输出，否则处于无效状态


#即使是对于感知机，也可以实现基于突触连接权重的修改的学习函数
#在训练阶段开始时，权重完全随机
#每次训练都会有一些正确例子，也有一些失败的例子
#如果分类正确就不给予改变，错误就改变突出权重以阐释改变网络的分类性能

#学习率（learning rate）：
#将输出误差反向传播给网络参数，以此来拟合样本的输出。
#本质上是最优化的一个过程，逐步趋向于最优解。
#但是每一次更新参数利用多少误差，就需要通过一个参数来控制，
#这个参数就是学习率（Learning rate）,也称为步长

#学习率越大，输出误差对参数的影响就越大，参数更新的就越快，
#但同时受到异常数据的影响也就越大，很容易发散。

#参考：
#https://www.cnblogs.com/zhanglei93/p/7871646.html

#感知机定理（perceptron theorem）：
#如果布尔函数（boolean function）是一个线性阈值函数（即，
#它是线性可分离的），那么局部感知机规则就可以找到一组
#能够以有限数量的步骤实现的权重


#4.3 线性分离

#当一组输出值可以被直线分割时，输出值被认为是线性可分离的
#布尔神经元的决策规则对应于由超平面操作的输入特征空间的分解

#如果除了输出神经元外，甚至神经网络的输入都是布尔型的
#那么使用神经网络执行分类相当于确定输入向量的布尔函数
#使用两个输入和输出神经元，可以以非常直观的方式表示与门、或门

#但是，某些布尔函数不能通过网络结构进行复制
#比如XOR或identity函数

#4.4 R中的感知机函数
#尝试通过感知机对鸢尾花进行分类
rm(list = ls(all = TRUE))
setwd("C:\\Users\\lenovo\\Desktop\\R\\神经网络_R语言实现")
#数据导入并查看
data(iris)
head(iris, n = 20)

#前100行为我们感兴趣的setosa与versicolor两个类别
iris_sub = iris[1:100, c(1,3,5)]
names(iris_sub) = c("sepal", "petal", "species")
head(iris_sub)

#加载ggplot2，然后使用ggplot2获得类别关于sepal.length
#与petal.length分布的散点图
library(ggplot2)

ggplot(iris_sub, aes(x = sepal, y = petal))+
  geom_point(aes(colour = species, shape = species), size = 3)+
  xlab("Sepal length")+
  ylab("Petal length")+
  ggtitle("Species vs Sepal and Petal lengths")

#可以看到，这两个类别置于平面的不同区域，因此线性分离是可能的
euclidean.norm = function(x){sqrt(sum(x*x))}

distance.from.plane = function(z,w,b){
  sum(z*w) + b
}

classify.linear = function(x,w,b){
  distances = apply(x, 1, distance.from.plane, w, b)
  return(ifelse(distances < 0, -1, +1))
}

perceptron = function(x, y, learning.rate = 1){
  w = vector(length = ncol(x)) #initialize weights
  b = 0 #Initialize bias
  k = 0 #count updates
  R = max(apply(x, 1, euclidean.norm))
  mark.complete = TRUE
  
  while (mark.complete) {
    mark.complete = FALSE
    yc = classify.linear(x,w,b)
    for (i in 1:nrow(x)) {
      if (y[i] != yc[i]){
        w = w + learning.rate * y[i] * x[i,]
        b = b + learning.rate * y[i] * R^2
        k = k + 1
        mark.complete = TRUE
      } 
    }
  }
  s = euclidean.norm(w)
  return(list(w = w/s, b = b/s, updates = k))
}

x = cbind(iris_sub$sepal, iris_sub$petal)

y = ifelse(iris_sub$species == "setosa", +1, -1)
#将setosa的输出设置为正值

#得出感知机的最优权重
p = perceptron(x,y)

plot(x, cex = 0.2)

points(subset(x,y == 1), col = "black", pch = "+", cex = 2)
points(subset(x,y == -1), col = "red", pch = "*", cex = 2)

#获得感知机的斜率与截距
intercept = - p$b / p$w[[2]]
slope = -p$w[[1]] / p$w[[2]]

#绘制线性分离线
abline(intercept, slope, col = "green")


#4.5 多层感知机

#与门和或门输出是线性可分离的，感知机可以用来模拟这个数据
#随着位数的增加，可实现的函数总数和比例趋于零
#如果采用异或门，线性分离器是不能实现的

#为了实现更广泛的函数，必须将输入层和输出层之间的中间传输引入感知机中
#它允许输入的某种内部表示，这被称为多层感知机

#在多层感知机中。除输入层节点外，每个节点都是
#使用非线性激活函数的神经元

#总之，多层和非线性区分了简单感知机与MLP

#XOR是使用三层网络实现的，并且是OR与AND感知机的组合


#4.6 使用RSNNS在R中实现MLP
#install.packages("RSNNS")

rm(list = ls(all = TRUE))
setwd("C:\\Users\\lenovo\\Desktop\\R\\神经网络_R语言实现")

#加载数据
data(iris)

library("RSNNS")

#将数据集iris在行内混洗，使得数据集中的行序为随机
iris = iris[sample(1:nrow(iris),length(1:nrow(iris))),1:ncol(iris)]

#分别设置自变量和目标变量并分配
irisValues = iris[,1:4]
irisTargets = decodeClassLabels(iris[,5])

#将训练数据与测试数据进行分割
#85%用于训练，15%用于测试
iris = splitForTrainingAndTest(irisValues, irisTargets, ratio = 0.15)
#数据被标准化
#使用normalizeData对inputsTrain进行归一化
#然后使用在这个标准化过程中获得的标准化参数标准化inputsTest
#如果没有设置参数dontNormTargets，则以相同的方式标准化目标
iris = normTrainingAndTestSet(iris)

#一个完全连接的前馈神经网络
#通过误差的反向传播或相关程序来进行训练
#测试数据集也被传递以用来提供测试结果
model = mlp(iris$inputsTrain,
            iris$targetsTrain,
            size = 5,
            learnFuncParams = c(0.1), #0.1，不是0, 1！
            maxit = 50,
            inputsTest = iris$inputsTest,
            targetsTest = iris$targetsTest)

#打印出网络的汇总
summary(model)
#提取对象rsnns的权重矩阵
weightMatrix(model)

#现在度量模型训练中算法的性能

#par(mfrow = c(2,2))

#绘制模型网络中的迭代误差
plotIterativeError(model)
#黑色为拟合迭代误差，红色为迭代测试误差

#进行预测
predictions = predict(model, iris$inputsTest)

#通过回归误差计算对它们的比较
plotRegressionError(predictions[,2], iris$targetsTest[,2])

#混淆矩阵
confusionMatrix(iris$targetsTrain, fitted.values(model))
confusionMatrix(iris$targetsTest, predictions)

#评估网络的工作性能
#par(mfrow = c(1,2))
plotROC(fitted.values(model)[,2], iris$targetsTrain[,2])
plotROC(predictions[,2], iris$targetsTest[,2])

confusionMatrix(iris$targetsTrain,
                encodeClassLabels(fitted.values(model),
                method = "402040",
                l = 0.4,
                h = 0.6))
